%{

/*
 * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 * See https://llvm.org/LICENSE.txt for license information.
 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 *
 */

/* Generate initialization for AST-to-ILM conversion tables.
 * The table includes the textual name of the ILM operator,
 * its numerical value, number of operands, and type of each operand.
 */

#include <stdio.h>
#include <stdlib.h>

FILE* outfile;
FILE* mytmpfile;

#define pilm 1
#define psym 2
#define pline 3
#define pdtype 4
#define pnum 5
#define pilms 6
#define psyms 7
#define pargs 8
#define pnums 9

#define chilm 'i'
#define chsym 's'
#define chline 'l'
#define chdtype 't'
#define chargs 'a'
#define chnum 'n'

void
putdecls()
{
    fprintf( outfile, 
	"/* upperilm.h - THIS FILE IS WRITTEN BY THE UTILITY PROGRAM UPPERL\n"
	" *              DO NOT EDIT THIS FILE MANUALLY */\n\n" );
    fprintf( outfile, 
	"typedef struct{\n"
	"    int ilmtype;\n"
	"    char* name;\n"
	"    int numoperands;\n"
	"    int* operand;\n"
	"}ilminfo;\n\n" );
    fprintf( outfile, "#define pilm %d\n", pilm );
    fprintf( outfile, "#define psym %d\n", psym );
    fprintf( outfile, "#define pline %d\n", pline );
    fprintf( outfile, "#define pdtype %d\n", pdtype );
    fprintf( outfile, "#define pnum %d\n", pnum );
    fprintf( outfile, "#define pilms %d\n", pilms );
    fprintf( outfile, "#define psyms %d\n", psyms );
    fprintf( outfile, "#define pargs %d\n", pargs );
    fprintf( outfile, "#define pnums %d\n", pnums );
    fprintf( outfile, "\n" );
    fprintf( outfile, "#define chilm '%c'\n", chilm );
    fprintf( outfile, "#define chsym '%c'\n", chsym );
    fprintf( outfile, "#define chline '%c'\n", chline );
    fprintf( outfile, "#define chdtype '%c'\n", chdtype );
    fprintf( outfile, "#define chnum '%c'\n", chnum );
    fprintf( outfile, "#define chargs '%c'\n", chargs );
    fprintf( outfile, "\n" );
}/* putdecls */

#define MAXCOMBO 100
#define MAXSTRING 100

char operation[MAXSTRING];
char ilmname[MAXSTRING];
int linenum;
int numoperands;
int numoperations = 0;
const char* operand[10];
int j;

int combinations = 0;
int combolen[MAXCOMBO];
char* combo[MAXCOMBO];

void
addcombination()
/* see if this combination of operands has been seen before */
{
    int i, l, lc;
    char c[MAXSTRING];
    if( numoperands == 0 ) return;
    strcpy( c, operand[0] );
    lc = strlen( c );
    for( i = 1; i < numoperands; ++i ){
	l = strlen( operand[i] );
	if( lc+l >= MAXSTRING ){
	    fprintf( stderr, "line %d: too many operands; increase MAXSTRING\n",
			linenum );
	    exit( 1 );
	}
	lc += l;
	strcat( c, operand[i] );
    }
    for( i = 0; i < combinations; ++i ){
	if( numoperands != combolen[i] ) continue;
	if( strcmp( c, combo[i] ) == 0 ) return; /* found it! */
    }
    /* didn't find it */
    ++combinations;
    if( combinations >= MAXCOMBO ){
	fprintf( stderr, "line %d: too many operand combinations; increase MAXCOMBO\n",
		linenum );
	exit( 1 );
    }
    combolen[combinations] = numoperands;
    combo[combinations] = (char*)malloc( lc + 1 );
    strcpy( combo[combinations], c );

    fprintf( outfile, "int OP%s[] = {", c );
    for( i = 0; i < numoperands; ++i ){
	if( i ){
	    fprintf( outfile, "," );
	}
	fprintf( outfile, " p%s", operand[i] );
    }
    fprintf( outfile, " };\n" );
    ++combinations;
}/* addcombination */


%}

%option nounistd
%option noyywrap
%option never-interactive
%s ILMname Operands
%%

<INITIAL>[A-Za-z0-9_]+ { /* operation name */
    /* check for alphabetical order */
    j = strcmp( operation, yytext );
    if( j > 0 ){
	fprintf( stderr, "line %d: operation %s should preceed operation %s\n",
		linenum, yytext, operation );
    }
    if( j == 0 ){
	fprintf( stderr, "line %d: operation %s appears twice\n",
		linenum, yytext );
    }
    strcpy( operation, yytext );
    strcpy( ilmname, yytext );
    numoperands = 0;
    BEGIN( Operands );
}
<INITIAL>[ \t]	{ /* ignore leading white space */; }
<INITIAL>\n	{ /* ignore blank line */
    ++linenum; 
}
<INITIAL>.	{ /* error for anything else */
    fprintf( stderr, "line %d: error at %s\n", linenum, yytext );
}

<Operands>"/"	{ /* slash means ILM name is different */
    if( numoperands == 0 ){
	BEGIN( ILMname );
    }else{
	fprintf( stderr, "line %d: slash must precede operands\n", linenum );
    }
}
<Operands>[ \t]+	{ /* ignore white space */; }
<Operands>args	{
    operand[numoperands] = "args";
    ++numoperands;
}
<Operands>dtype	{
    operand[numoperands] = "dtype";
    ++numoperands;
}
<Operands>ilm	{
    operand[numoperands] = "ilm";
    ++numoperands;
}
<Operands>ilms	{
    operand[numoperands] = "ilms";
    ++numoperands;
}
<Operands>line	{
    operand[numoperands] = "line";
    ++numoperands;
}
<Operands>num	{
    operand[numoperands] = "num";
    ++numoperands;
}
<Operands>nums	{
    operand[numoperands] = "nums";
    ++numoperands;
}
<Operands>sym	{
    operand[numoperands] = "sym";
    ++numoperands;
}
<Operands>syms	{
    operand[numoperands] = "syms";
    ++numoperands;
}
<Operands>[a-zA-Z0-9_]+	{ /* unknown type */
    fprintf( stderr, "line %d: unknown operand %s\n", linenum, yytext );
}
<Operands>.	{ /* unknown character */
    fprintf( stderr, "line %d: unexpected %s\n", linenum, yytext );
}
<Operands>\n	{ /* end of line means output it all */
    fprintf( mytmpfile, "    IM_%s, \"%s\", %d, ",
		ilmname, operation, numoperands );
    if( numoperands == 0 ){
	fprintf( mytmpfile, "NULL," );
    }else{
	int i;
	fprintf( mytmpfile, "OP" );
	for( i = 0; i < numoperands; ++i ){
	    fprintf( mytmpfile, "%s", operand[i] );
	}
	fprintf( mytmpfile, "," );
	addcombination();
    }
    fprintf( mytmpfile, "\n" );
    ++numoperations;
    /* read next line */
    ++linenum;
    BEGIN( INITIAL );
}

<ILMname>[A-Za-z0-9_]+ {
    strcpy( ilmname, yytext );
    BEGIN( Operands );
}
<ILMname>[ \t]	{ /* ignore white space */; }
<ILMname>[.]	{
    fprintf( stderr, "line %d: error at %s\n", linenum, yytext );
}
<ILMname>\n	{ /* mustn't start next line here */ 
    fprintf( stderr, "line %d: line ends after /", linenum );
    ++linenum;
    BEGIN( INITIAL );
}

%%

int 
main( int argc, char* argv[] )
{
    int ch; /* Must be signed (getc() and fputc()) */

    if( argc > 1 ){
	yyin = fopen( argv[1], "r" );
    }
    if( argc > 2 ){
	outfile = fopen( argv[2], "wb" );
	if( outfile == NULL ){
	    fprintf( stderr, "Could not open output file %s\n", argv[2] );
	    exit( 1 );
	}
    }else{
	outfile = stdout;
    }
    /* used to call tmpfile() */
    mytmpfile = fopen( "TMPFILE", "wb" );
    putdecls();
    linenum = 1;
    strcpy( operation, "" );
    yylex();
    /* copy temp stuff to outfile */
    fprintf( outfile, "\nilminfo info[] = {\n" );
    /* should be able to, and used to be able to,
     * simply rewind mytmpfile here; this now fails on rainier */
    fclose( mytmpfile );
    mytmpfile = fopen( "TMPFILE","r" );
    while( (ch = getc(mytmpfile)) != EOF ){
	fputc( ch, outfile );
    }
    fprintf( outfile, "    0, NULL, 0, NULL };\n" );
    fprintf( outfile, "\n#define NUMOPERATIONS %d\n", numoperations );
    return 0;
}/* main */
